package com.ruge.user.service.impl;

import com.ruge.core.response.Response;
import com.ruge.core.tool.AssertTool;
import com.ruge.core.tool.RedisKeyTool;
import com.ruge.tool.date.DateTool;
import com.ruge.tool.str.StringTool;
import com.ruge.user.domain.vo.SignInfoDetailVo;
import com.ruge.user.domain.vo.SignInfoVo;
import com.ruge.user.error.UserError;
import com.ruge.user.service.SignService;
import org.springframework.data.redis.connection.BitFieldSubCommands;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.time.Instant;
import java.time.LocalDate;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * @author ruge.wu
 * @since 2021/12/20 15:02
 */
@Service
public class SignServiceImpl implements SignService {
    @Resource
    private RedisTemplate redisTemplate;

    /**
     * 签名
     */
    @Override
    public Response doSign(Long aid, String date) {
        // 获取日期
        Date signDate = DateTool.getDate(date, "yyyyMMdd");
        // 构建redis key
        String signKey = RedisKeyTool.buildSignKey(aid, signDate);
        //获取日期对应的天数 多少号
        int day = DateTool.getDefaultLocalDateTime(signDate.getTime()).getDayOfMonth() - 1;
        //查看是否已签到
        Boolean isSigned = redisTemplate.opsForValue().getBit(signKey, day);
        AssertTool.mustTrue(!isSigned, UserError.USER_HAS_SIGN);
        // 签到
        redisTemplate.opsForValue().setBit(signKey, day, true);
        return Response.create();
    }

    /**
     * 清除签到记录
     */
    @Override
    public Response unSign(Long aid, String date) {
        // 获取日期
        Date signDate = DateTool.getDate(date, "yyyyMMdd");
        // 构建redis key
        String signKey = RedisKeyTool.buildSignKey(aid, signDate);
        Long aLong = DateTool.getLongByString(date, "yyyyMMdd");
        int day = DateTool.getDefaultLocalDateTime(aLong).getDayOfMonth() - 1;
        redisTemplate.opsForValue().setBit(signKey, day, false);
        return Response.create();
    }

    /**
     * 获取签到信息
     *
     * @param aid  aid
     * @param date yyyyMMdd
     * @return 获取签到信息
     */
    @Override
    public Response getInfo(Long aid, String date) {
        // 获取日期
        Date signDate = DateTool.getDate(date, "yyyyMMdd");
        String signKey = RedisKeyTool.buildSignKey(aid, signDate);
        // 查看当月签到次数
        Long monthCount = (Long) redisTemplate.execute((RedisCallback<Long>) con -> con.bitCount(signKey.getBytes()));
        // 查看连续签到次数
        Long continueCount = 0L;
        //查看指定日期是否签到
        int day = DateTool.getDefaultLocalDateTime(signDate.getTime()).getDayOfMonth() - 1;
        // 查看今天是否签到
        Boolean today = redisTemplate.opsForValue().getBit(signKey, day);
        int dayOfMonth = DateTool.getDefaultLocalDateTime().getDayOfMonth();
        List<Long> list = bitFiled(signKey, dayOfMonth, 0);
        if (StringTool.isNotBlank(list)) {
            Long v = list.get(0);
            // 位移计算连续签到
            for (int i = dayOfMonth; i > 0; i--) {
                // 最后一位是0  视为未签到
                if (v >> 1 << 1 == v) {
                    // 当天未签到 略过
                    if (i != dayOfMonth) {
                        break;
                    }
                } else {
                    continueCount++;
                    // 右移一位并重新赋值
                    v = v >> 1;
                }
            }
        }
        SignInfoVo build = SignInfoVo.builder()
                .aid(aid)
                .monthCount(monthCount)
                .continueCount(continueCount)
                .today(today)
                .build();
        return Response.create(build);
    }

    /**
     * 获取月签到信息
     *
     * @param aid  aid
     * @param date yyyyMM
     * @return 获取签到信息
     */
    @Override
    public Response<List<SignInfoDetailVo>> getDetail(Long aid, String date) {
        // 获取日期
        Date signDate = DateTool.getDate(date, "yyyyMMdd");
        String signKey = RedisKeyTool.buildSignKey(aid, signDate);

        int dayOfMonth = DateTool.getDefaultLocalDateTime(DateTool.getMonthEnd(signDate.getTime())).getDayOfMonth();
        List<Long> list = bitFiled(signKey, dayOfMonth, 0);
        // 查看本月签到详情
        List<SignInfoDetailVo> signInfoDetailVos = new ArrayList<>();

        if (StringTool.isNotBlank(list)) {
            Long v = list.get(0);
            // 位移计算连续签到
            for (int i = dayOfMonth; i > 0; i--) {
                LocalDate localDate = signDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDate().withDayOfMonth(i);
                ZonedDateTime zonedDateTime = localDate.atStartOfDay(ZoneId.systemDefault());
                Instant instant = zonedDateTime.toInstant();
                Date from = Date.from(instant);
                // 左移右移 不相等 视为已签到
                boolean flag=v>> 1 <<1 !=v;
                String dateInstance = DateTool.getDateInstance(from, "yyyy-MM-dd");
                signInfoDetailVos.add(SignInfoDetailVo.builder()
                        .date(dateInstance)
                        .flag(flag)
                        .build());
                v = v >> 1;
            }
        }
        return Response.create(signInfoDetailVos);
    }

    public List<Long> bitFiled(String signKey, int limit, int offset) {
        return (List<Long>) redisTemplate.opsForValue().bitField(signKey,
                BitFieldSubCommands.create().get(BitFieldSubCommands.BitFieldType.unsigned(limit)).valueAt(offset));
    }


}
